/**
 * 
 */
package com.goldentrail.dataobject;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import org.json.JSONException;
import org.json.JSONObject;

/**
 * @author dey
 *
 */
public class Run {

	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "Run [runId=" + runId + ", invocations=" + invocations + "]";
	}

	private String    runId     = null;
	private String    wfId      = null;
	private ArrayList<Invocation>  invocations = null;	
	private ArrayList<Run>    nextRunId = null;
	private ArrayList<Run>    prevRunId = null;

	private ArrayList<Node> nodes     = null; //currently only rightmost nodes in run (or query resut)
	private String nodeUri = null;

	/**
	 * @return the invocations
	 */
	public ArrayList<Invocation> getInvocations() {
		
		ArrayList<Invocation>  newInvocations = new ArrayList<Invocation>();
		Hashtable<String, DataArtifact> inDataHt;
		Hashtable<String, DataArtifact> outDataHt;
		Invocation invoc;
		String key;
		
		for(Invocation i : invocations) {
			
			invoc = new Invocation();
			inDataHt = new Hashtable<String, DataArtifact>();
			outDataHt = new Hashtable<String, DataArtifact>();
			
			Enumeration<String> ein = i.getInputData().keys();
	    	while (ein.hasMoreElements()) {
	    		key = ein.nextElement();
	    		if (key.contains(":")) {
	    			inDataHt.put(getId(key), getData(getId(key), i.getInputData().get(key)));
	    		} else {
	    			inDataHt.put(key, i.getInputData().get(key));
	    		}
	    	}
	    	
			Enumeration<String> eout = i.getOutputData().keys();
	    	while (eout.hasMoreElements()) {
	    		key = eout.nextElement();
	    		if (key.contains(":")) {
	    			outDataHt.put(getId(key), getData(getId(key), i.getOutputData().get(key)));
	    		} else {
	    			outDataHt.put(key, i.getOutputData().get(key));
	    		}
	    	}
	    	
	    	invoc.setInvocationId(i.getInvocationId());
	    	invoc.setNodeUri(i.getNodeUri());
	    	invoc.setType(i.getType());
	    	invoc.setInputData(inDataHt);
	    	invoc.setOutputData(outDataHt);
	    	newInvocations.add(invoc);
		}
		
		return newInvocations;
	}

	/**
	 * @param invocations the invocations to set
	 */
	public DataArtifact getData(String key, DataArtifact data) {

		DataArtifact newData = new DataArtifact();
		newData.setDataArtifactId(key);
		newData.setNodeUri(data.getNodeUri());
		newData.setValue(data.getValue());
		newData.setValueType(data.getValueType());
		
		return newData;	
	}
	
	/**
	 * @param invocations the invocations to set
	 */
	public String getId(String key) {
		String[] dataAndRunId;
		String dataId;
		dataAndRunId = key.split(":");
		if (dataAndRunId.length > 1) {
			dataId = dataAndRunId[1];	
		} else {
			dataId = dataAndRunId[0];
		}
		return dataId;
	}
	

	
	
	/**
	 * @param invocations the invocations to set
	 */
	public void setInvocations(ArrayList<Invocation> invocations) {
		this.invocations = invocations;
	}

	/**
	 * @return the runId
	 */
	public String getRunId() {
		return runId;
	}

	/**
	 * @param runId the runId to set
	 */
	public void setRunId(String runId) {
		this.runId = runId;
	}

	/**
	 * @return the wfId
	 */
	public String getWfId() {
		return wfId;
	}

	/**
	 * @param wfId the wfId to set
	 */
	public void setWfId(String wfId) {
		this.wfId = wfId;
	}

	/**
	 * @return the nodes
	 */
	public ArrayList<Node> getNodes() {
		return nodes;
	}

	/**
	 * @param nodes the nodes to set
	 */
	public void setNodes(ArrayList<Node> nodes) {
		this.nodes = nodes;
	}

	/**
	 * @return the nextRunId
	 */
	public ArrayList<Run> getNextRunId() {
		return nextRunId;
	}

	/**
	 * @param nextRunId the nextRunId to set
	 */
	public void setNextRunId(ArrayList<Run> nextRunId) {
		this.nextRunId = nextRunId;
	}

	/**
	 * @return the prevRunId
	 */
	public ArrayList<Run> getPrevRunId() {
		return prevRunId;
	}

	/**
	 * @param prevRunId the prevRunId to set
	 */
	public void setPrevRunId(ArrayList<Run> prevRunId) {
		this.prevRunId = prevRunId;
	}

	/**
	 * 
	 */
	public Run() {
		// TODO Auto-generated constructor stub
	}

	public String getJSONString() {
		JSONObject json = new JSONObject();
		try {
			json.put("id", getRunId());
			json.put("type", "run");
			
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String ret = json.toString();
		return ret;
	}
	
	public static Run fromJSON(JSONObject json) {
		Run run = new Run();
		try {
			run.setRunId(json.getString("id"));
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return run;
	}
	
	public static Run fromJSON(String jsonString) {
		Run run = new Run();
		try {
			JSONObject json = new JSONObject(jsonString);
			run.setRunId(json.getString("id"));
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return run;
	}
	
	/**
	 * @return the nodeUri. For internal use in Neo4j
	 */
	public String getNodeUri() {
		return nodeUri;
	}

	/**
	 * @param nodeUri
	 *            the nodeUri to set. For internal use in Neo4j
	 */
	public void setNodeUri(String nodeUri) {
		this.nodeUri = nodeUri;
	}

	/**
	 * Checks equivalence based on nodeUris if available, else just uses
	 * runId - not trustworthy if nodeUri not available, which is only
	 * when the node was returned from the database
	 * 
	 * @param invoc
	 *            the invocation to check equivalence against
	 * @return true if equal, false otherwise
	 */
	public boolean equals(Run r) {
		if (this.getNodeUri() != null && r.getNodeUri() != null) {
			return this.getNodeUri().equals(r.getNodeUri());
		} else
			return this.getRunId().equals(r.getRunId());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object o) {
		if (o instanceof Run)
			return this.equals((Run) o);
		else
			return false;
	}
}
